package com.tiancheng.trade.payment.channel.umsmop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tiancheng.commom.pay.PayOrderAutoQueryConfig;
import com.tiancheng.trade.commom.core.data.Result;
import com.tiancheng.trade.commom.core.enums.ClearingCycleModeEnum;
import com.tiancheng.trade.commom.core.enums.PaySceneEnum;
import com.tiancheng.trade.commom.core.enums.PaymentChannelEnum;
import com.tiancheng.trade.commom.core.enums.PaymentWayEnum;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.commom.web.cache.model.MerchantBO;
import com.tiancheng.trade.commom.web.feign.MerchantFeignService;
import com.tiancheng.trade.commom.web.http.PlatformRequest;
import com.tiancheng.trade.commom.core.utils.DateTimeUtil;
import com.tiancheng.trade.payment.channel.AbstractPayChannel;
import com.tiancheng.trade.payment.channel.bo.*;
import com.tiancheng.trade.payment.channel.enums.*;
import com.tiancheng.trade.payment.channel.ums.params.*;
import com.tiancheng.trade.payment.channel.umsmop.request.*;
import com.tiancheng.trade.payment.channel.umsmop.response.*;
import com.tiancheng.trade.payment.channel.umsmop.util.UmsMopHttpUtils;
import com.tiancheng.trade.payment.enums.*;
import com.tiancheng.trade.commom.web.model.request.MerchantChannelRequestDTO;
import com.tiancheng.trade.commom.web.model.response.SettlementRuleResVO;
import com.tiancheng.trade.payment.mapper.PaymentFlowDetailMapper;
import com.tiancheng.trade.payment.mapper.PaymentOrderMapper;
import com.tiancheng.trade.payment.mapper.PaymentRefundFlowMapper;
import com.tiancheng.trade.payment.model.*;
import com.tiancheng.trade.payment.service.SettlePlatformService;
import com.tiancheng.trade.payment.util.CommonUtil;
import com.tiancheng.trade.payment.util.ObjectConvertUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 银联支付渠道
 */
@Slf4j
@Service
public class UmsMopPayChannel implements AbstractPayChannel {
    @Autowired
    private UmsMopAllConfig umsMopAllConfig;
    @Autowired
    private MerchantFeignService merchantFeignService;
    @Resource
    private PlatformRequest platformRequest;
    @Autowired
    private PaymentFlowDetailMapper paymentFlowDetailMapper;
    @Autowired
    private PaymentRefundFlowMapper paymentRefundFlowMapper;
    @Autowired
    private SettlePlatformService settlePlatformService;
    @Autowired
    private PaymentOrderMapper paymentOrderMapper;


    @Override
    public PaymentChannelEnum getPaymentChannelEnum() {
        return PaymentChannelEnum.UMS_MOP;
    }

    /**
     * 银联商务统一下单
     *
     * @param unifiedOrderBO {@link UnifiedOrderBO}
     * @return {@link Result<UnifiedOrderResBO>}
     * @throws BusinessException
     */
    @Override
    public Result<UnifiedOrderResBO> unifiedOrder(UnifiedOrderBO unifiedOrderBO) throws BusinessException {
        UmsMopUnifiedOrderRequest umsUnifiedOrderParam = new UmsMopUnifiedOrderRequest();
        final UmsMopProperties umsMopProperties = umsMopAllConfig.getByOrgPayChannelId(unifiedOrderBO.getOrgPaymentChannelId());

        handlePayWay(umsUnifiedOrderParam, unifiedOrderBO, umsMopProperties); // 不同支付方式处理
        umsUnifiedOrderParam.setRequestTimestamp(DateTimeUtil.temporalFormat2String(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));
        // 扫码支付和其他支付上送参数字段有差异
        if (PaySceneEnum.SCAN.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene())) {
            umsUnifiedOrderParam.setBillNo(unifiedOrderBO.getOrderId());
            umsUnifiedOrderParam.setBillDate(umsUnifiedOrderParam.getRequestTimestamp().substring(0, 10));
            umsUnifiedOrderParam.setBillDesc(CommonUtil.orderNameHandle(unifiedOrderBO.getOrderName()));
            umsUnifiedOrderParam.setWalletOption("SINGLE");
            umsUnifiedOrderParam.setQrCodeId(unifiedOrderBO.getOrderId());
            umsUnifiedOrderParam.setExpireTime(DateTimeUtil.temporalFormat2String(LocalDateTime.now().plusMinutes(30L), "yyyy-MM-dd HH:mm:ss")); // 二维码有效时间
        } else {
            umsUnifiedOrderParam.setMerOrderId(unifiedOrderBO.getFlowId());
            if (PaymentWayEnum.UNIONPAY.getCode().equalsIgnoreCase(unifiedOrderBO.getPayWay()) && unifiedOrderBO.getOrderName().length() > 15) {
                umsUnifiedOrderParam.setOrderDesc(CommonUtil.orderNameHandle(unifiedOrderBO.getOrderName().substring(0, 12)) + "...");
            } else {
                umsUnifiedOrderParam.setOrderDesc(CommonUtil.orderNameHandle(unifiedOrderBO.getOrderName()));
            }
        }
        umsUnifiedOrderParam.setPlatformAmount(0);
        // 子订单商品信息-商品维度清分
        List<UmsMopUnifiedOrderRequest.UmsProduct> goods = new ArrayList<>();
        List<UnifiedOrderResBO.SubOrder> subOrders = new ArrayList<>();
        unifiedOrderBO.getSubOrders().forEach(subOrderBO -> {
            UmsMopUnifiedOrderRequest.UmsProduct umsProduct = new UmsMopUnifiedOrderRequest.UmsProduct();
            umsProduct.setGoodsId(subOrderBO.getProductCode());
            // TODO 微信、支付宝支付需要限制商品id不超过32位
            umsProduct.setGoodsId(CommonUtil.subString(subOrderBO.getProductCode(), 32));
            umsProduct.setGoodsName(CommonUtil.orderNameHandle(subOrderBO.getProductName()));
            umsProduct.setQuantity(subOrderBO.getProductCount());
            umsProduct.setPrice(subOrderBO.getAmount()); // 传实付金额
            umsProduct.setSubMerchantId(subOrderBO.getSubMerchantId());
            umsProduct.setMerOrderId(subOrderBO.getSubFlowId());
            umsProduct.setSubOrderAmount(subOrderBO.getAmount());
            goods.add(umsProduct);
            UnifiedOrderResBO.SubOrder subOrder = ObjectConvertUtil.convert(umsProduct, UnifiedOrderResBO.SubOrder.class).get();
            subOrder.setEnterpriseCode(subOrderBO.getEnterpriseCode());
            subOrder.setSubOrderId(subOrderBO.getSubOrderId());
            subOrders.add(subOrder);
        });
        // 平台分账金额
        AtomicInteger platformAmount = new AtomicInteger();
        UnifiedSettleMerchantBO settleMerchantBO = genSettleInfo(unifiedOrderBO);
        // 分账方列表
        final List<UmsMopUnifiedOrderRequest.SubOrder> subOrderList = settleMerchantBO.getMerchantItems().stream().map(s -> {
            final UmsMopUnifiedOrderRequest.SubOrder subOrder = new UmsMopUnifiedOrderRequest.SubOrder();
            subOrder.setMid(s.getMerId());
            subOrder.setMerOrderId(s.getMerOrderId());
            subOrder.setTotalAmount(s.getSatAmt());
            return subOrder;
        }).toList();

        //umsUnifiedOrderParam.setDivisionFlag(true);
        // 手续费比例
        umsUnifiedOrderParam.setFeeRatio("0");
        umsUnifiedOrderParam.setCostSubsidy("true");
        //umsUnifiedOrderParam.setGoods(goods);
        //umsUnifiedOrderParam.setSubOrders(subOrderList);
        // 商品标记，用于优惠活动(微信)
        umsUnifiedOrderParam.setGoodsTag(unifiedOrderBO.getGoodsTag());

        Optional.ofNullable(unifiedOrderBO.getAmount()).ifPresent(umsUnifiedOrderParam::setOriginalAmount);

        Assert.isTrue(null != unifiedOrderBO.getAmount(), "订单支付总金额有误");
        umsUnifiedOrderParam.setTotalAmount(unifiedOrderBO.getAmount());
        // TODO 分账方案确认
        //umsUnifiedOrderParam.setDivisionFlag(true);// 是否分账
        //umsUnifiedOrderParam.setAsynDivisionFlag(true);// 异步分账
        umsUnifiedOrderParam.setDivisionFlag(false);// 是否分账
        umsUnifiedOrderParam.setAsynDivisionFlag(false);// 异步分账
        umsUnifiedOrderParam.setPlatformAmount(platformAmount.get());
        umsUnifiedOrderParam.setNotifyUrl(umsMopProperties.getPayCallback());
        umsUnifiedOrderParam.setReturnUrl(unifiedOrderBO.getReturnUrl());
        umsUnifiedOrderParam.setSignType("SHA256");

        final String umsMopUrl = getUmsMopUrl(unifiedOrderBO.getPayScene(), unifiedOrderBO.getPayWay());
        //umsUnifiedOrderParam.setSign(buildSignString(umsMopProperties.getKey(), umsUnifiedOrderParam));
        log.info("ums_mop_unifiedOrder_url:{}, params:{}", umsMopUrl, JsonUtil.toJsonString(umsUnifiedOrderParam));
        Result<UnifiedOrderResBO> result = new Result<>();

        // 微信公众号支付、h5支付直接返回银联支付请求数据，由前端执行支付请求
        if (PaySceneEnum.WXAPP.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene()) ||
                PaySceneEnum.H5.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene().toLowerCase())) {

            UnifiedOrderResBO unifiedOrderResBO = new UnifiedOrderResBO() {{
                setTransactionId("");
                setOutTradeNo("");
            }};

            unifiedOrderResBO.setPayInfo(
                    JsonUtil.toJsonString(
                            new HashMap<String, Object>() {{
                                put("payUrl", umsMopProperties.getApiUrlH5Wx());
                                put("requestMethod", "get");
                                put("requestParam", umsUnifiedOrderParam);
                            }}
                    )
            );
            unifiedOrderResBO.setSubOrders(subOrders);
            result.setData(unifiedOrderResBO);

            return result;
        }

        try {

            final UmsMopUnifiedOrderResponse response = new UmsMopHttpUtils<UmsMopUnifiedOrderResponse>(umsMopProperties,
                    umsMopUrl).doPostToken(JsonUtil.toJsonString(umsUnifiedOrderParam), UmsMopUnifiedOrderResponse.class);
            log.info("银联mop统一下单返回:{}", JsonUtil.toJsonString(response));
            // 处理相响应结果
            result.setRet(response.isOK() ? 0 : 1);
            result.setMsg(StringUtils.isNotEmpty(response.getErrInfo()) ? response.getErrInfo() : response.getErrMsg());

            UnifiedOrderResBO unifiedOrderResBO = new UnifiedOrderResBO();
            unifiedOrderResBO.setTransactionId(response.getSeqId() == null ? "" : response.getSeqId());
            unifiedOrderResBO.setOutTradeNo(response.getTargetOrderId() == null ? "" : response.getTargetOrderId());
            unifiedOrderResBO.setSubOrders(subOrders);
            // app中支付
            if (PaySceneEnum.APP.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene())) {
                if (null != response.getJsPayRequest() && response.getJsPayRequest().size() > 0) {
                    unifiedOrderResBO.setPayInfo(JsonUtil.toJsonString(response.getJsPayRequest()));
                }
                if (null != response.getAppPayRequest() && response.getAppPayRequest().size() > 0) {
                    if (PaymentWayEnum.UNIONPAY.getCode().equalsIgnoreCase(unifiedOrderBO.getPayWay())) {
                        // app-云闪付特殊处理
                        unifiedOrderResBO.setPayInfo(String.valueOf(response.getAppPayRequest().get("tn")));
                    } else {
                        unifiedOrderResBO.setPayInfo(JsonUtil.toJsonString(response.getAppPayRequest()));
                    }
                }
                result.setData(unifiedOrderResBO);
                return result;
            }

            // 微信小程序
            if (PaySceneEnum.MINIAPP.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene())) {
                if (null != response.getMiniPayRequest() && response.getMiniPayRequest().size() > 0) {
                    unifiedOrderResBO.setPayInfo(JsonUtil.toJsonString(response.getMiniPayRequest()));
                }
                result.setData(unifiedOrderResBO);
                return result;
            }
            // 云闪付小程序
            if (PaySceneEnum.UNION_MINI.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene())) {
                if (null != response.getMiniPayRequest() && response.getMiniPayRequest().size() > 0) {
                    unifiedOrderResBO.setPayInfo(String.valueOf(response.getMiniPayRequest().get("tn")));
                }
                result.setData(unifiedOrderResBO);
                return result;
            }
            // 扫码支付
            if (PaySceneEnum.SCAN.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene())) {
                if (!response.getBillQRCode().isEmpty()) {
                    unifiedOrderResBO.setPayInfo(response.getBillQRCode());
                }
            }
            result.setData(unifiedOrderResBO);
        } catch (Exception e) {
            log.error(e.getMessage());
            result.setRet(1);
            result.setMsg("下单支付失败");
        }
        return result;
    }

    /**
     * 下单查询
     *
     * @param getUnifiedOrderBO {@link GetUnifiedOrderBO}
     * @return {@link Result<GetUnifiedOrderResBO>}
     * @throws BusinessException
     */
    @Override
    public Result<GetUnifiedOrderResBO> get(GetUnifiedOrderBO getUnifiedOrderBO) throws BusinessException {
        final UmsMopProperties umsMopProperties = umsMopAllConfig.getByOrgPayChannelId(getUnifiedOrderBO.getOrgPaymentChannelId());
        UmsMopGetUnifiedOrderRequest umsGetUnifiedOrderParam = new UmsMopGetUnifiedOrderRequest();
        umsGetUnifiedOrderParam.setRequestTimestamp(DateTimeUtil.temporalFormat2String(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));

        QueryWrapper<PaymentOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("flow_id", getUnifiedOrderBO.getOrderId());
        queryWrapper.eq("user_id", getUnifiedOrderBO.getUid());
        PaymentOrder paymentOrder = paymentOrderMapper.selectOne(queryWrapper);
        Assert.isTrue(null != paymentOrder, "未查询到相应流水");
        Assert.isTrue(!StringUtils.isEmpty(paymentOrder.getPaymentWay()), "流水支付方式异常");

        Map<String, String> mtiMap = handleMidTidInstMid(paymentOrder.getPayScene(), paymentOrder.getPaymentWay(), umsMopProperties);
        umsGetUnifiedOrderParam.setMid(mtiMap.get("mid"));
        umsGetUnifiedOrderParam.setTid(mtiMap.get("tid"));
        umsGetUnifiedOrderParam.setInstMid(mtiMap.get("instMid"));
        umsGetUnifiedOrderParam.setMerOrderId(getUnifiedOrderBO.getOrderId());

        log.info("ums_getUnifiedOrder_url:{}, params:{}", umsMopProperties.getApiUrl(), JsonUtil.toJsonString(umsGetUnifiedOrderParam));
        Result<GetUnifiedOrderResBO> result = new Result<>();
        try {

            final UmsMopGetUnifiedOrderResponse getResult = new UmsMopHttpUtils<UmsMopGetUnifiedOrderResponse>(umsMopProperties, "v1/netpay/query").doPostToken(JsonUtil.toJsonString(umsGetUnifiedOrderParam),
                    UmsMopGetUnifiedOrderResponse.class);

            log.info("银联支付查询返回:{}", JsonUtil.toJsonString(getResult));
            // 处理相响应结果
            // 处理响应结果
            result.setRet(getResult.isOK() ? 0 : 1);
            result.setMsg(StringUtils.isNotEmpty(getResult.getErrInfo()) ? getResult.getErrInfo() : getResult.getErrMsg());

            GetUnifiedOrderResBO getUnifiedOrderResBO = new GetUnifiedOrderResBO();
            getUnifiedOrderResBO.setOrderId(getResult.getMerOrderId());
            getUnifiedOrderResBO.setTransactionId(getResult.getSeqId());
            getUnifiedOrderResBO.setTradeNo(getResult.getTargetOrderId());

            // 订单状态处理
            getUnifiedOrderResBO.setOrderStatus(ChannelOrderStatusEnum.fail);
            switch (UmsOrderStatusEnum.getByCode(getResult.getStatus())) {
                case NEW_ORDER:
                case WAIT_BUYER_PAY:
                    getUnifiedOrderResBO.setOrderStatus(ChannelOrderStatusEnum.wait);
                    break;
                case UNKNOWN:
                case TRADE_CLOSED:
                case TRADE_REFUND:
                    getUnifiedOrderResBO.setOrderStatus(ChannelOrderStatusEnum.fail);
                    break;
                case TRADE_SUCCESS:
                    getUnifiedOrderResBO.setOrderStatus(ChannelOrderStatusEnum.success);
                    break;
            }
            if (null != getResult.getPayTime()) {
                getUnifiedOrderResBO.setPayTime(DateTimeUtil.string2LocalDateTime(getResult.getPayTime(), "yyyy-MM-dd HH:mm:ss"));
            }
            if (null != getResult.getTotalAmount()) {
                getUnifiedOrderResBO.setAmount(getResult.getTotalAmount());
            }

            result.setData(getUnifiedOrderResBO);
        } catch (Exception e) {
            log.error(e.getMessage());
            result.setRet(1);
            result.setMsg("查询订单失败");
        }
        return result;
    }

    /**
     * 退款
     *
     * @param refundOrderBO {@link RefundOrderBO}
     * @return {@link Result<RefundOrderResBO>}
     * @throws BusinessException
     */
    @Override
    public Result<RefundOrderResBO> refund(RefundOrderBO refundOrderBO) throws BusinessException {
        UmsMopRefundOrderRequest umsRefundOrderParam = new UmsMopRefundOrderRequest();
        final UmsMopProperties umsMopProperties = umsMopAllConfig.getByOrgPayChannelId(refundOrderBO.getOrgPaymentChannelId());
        umsRefundOrderParam.setRequestTimestamp(DateTimeUtil.temporalFormat2String(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));

        QueryWrapper<PaymentOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("flow_id", refundOrderBO.getOrderId());
        queryWrapper.eq("user_id", refundOrderBO.getUid());
        PaymentOrder paymentOrder = paymentOrderMapper.selectOne(queryWrapper);

        Assert.isTrue(null != paymentOrder, "未查询到相应流水");
        Assert.isTrue(!StringUtils.isEmpty(paymentOrder.getPaymentWay()), "流水支付方式异常");

        Map<String, String> mtiMap = handleMidTidInstMid(paymentOrder.getPayScene(), paymentOrder.getPaymentWay(), umsMopProperties);
        umsRefundOrderParam.setMid(mtiMap.get("mid"));
        umsRefundOrderParam.setTid(mtiMap.get("tid"));
        if (PaySceneEnum.SCAN.getCode().equalsIgnoreCase(paymentOrder.getPayScene())) {
            umsRefundOrderParam.setBillDate(paymentOrder.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            umsRefundOrderParam.setBillNo(refundOrderBO.getOrderId());
        } else {
            umsRefundOrderParam.setMerOrderId(refundOrderBO.getOrderId());
        }

        Assert.isTrue(null != refundOrderBO.getAmount() && null != refundOrderBO.getOrderAmount(), "退款金额异常");

        umsRefundOrderParam.setRefundDesc(refundOrderBO.getReason());

        // refundOrderBO.getAmount()=总退款金额=退平台优惠券+退渠道优惠+退客户
        // 非全额退款需要传以下参数
        if (refundOrderBO.getAmount().intValue() != refundOrderBO.getOrderAmount() || (paymentOrder.getOrderAmount() > refundOrderBO.getOrderAmount())) {
            // 不退平台费
            umsRefundOrderParam.setPlatformAmount(0);

            Set<String> subOrderIds = refundOrderBO.getSubOrders().stream().map(RefundOrderBO.SubOrderBO::getSubOrderId).collect(Collectors.toSet());
            QueryWrapper<PaymentFlowDetail> conditions = new QueryWrapper<>();
            conditions.eq("flow_id", refundOrderBO.getOrderId()).in("sub_order_id", subOrderIds);
            List<PaymentFlowDetail> flowDetails = paymentFlowDetailMapper.selectList(conditions);

            // 子订单流水 key=subOrderId, value=subOrderFlowId
            Map<String, String> subFlowIdMap = new HashMap<>();
            // 商户进件号处理相关-1
            List<MerchantChannelRequestDTO> merchantChannelRequestDTOS = new ArrayList<>();
            for (PaymentFlowDetail flowDetail : flowDetails) {
                subFlowIdMap.put(flowDetail.getSubOrderId(), flowDetail.getSubFlowId());
                merchantChannelRequestDTOS.add(new MerchantChannelRequestDTO(flowDetail.getMerchantCode(), refundOrderBO.getPaymentChannelCode(), refundOrderBO.getOrgPaymentChannelId(), refundOrderBO.getBusinessCode()));
            }

            // 商户进件号处理相关-2
            Map<String, MerchantBO> merchantMap = settlePlatformService.getMerchantInfo(merchantChannelRequestDTOS);// key=merchantCode, value=商户进件号
            Map<String, String> subMerchantMap = new HashMap<>(); // key=subOrderId, value=商户进件号
            // 商户进件号处理相关-3、清分规则处理相关-3
            for (PaymentFlowDetail detail : flowDetails) {
                subMerchantMap.put(detail.getSubOrderId(), merchantMap.get(detail.getMerchantCode()).getSubAppid());
            }

            // 提高垃圾回收效率
            flowDetails.clear();
            flowDetails = null;
            merchantChannelRequestDTOS.clear();
            merchantChannelRequestDTOS = null;
            merchantMap.clear();
            merchantMap = null;
            Result<RefundOrderResBO> result = refundWithSubOrders(umsRefundOrderParam, refundOrderBO, subMerchantMap, subFlowIdMap, umsMopProperties);
            log.info("部分退款，部分多个子订单退款-同步退款。");
            RefundOrderResBO refundOrderResBO = new RefundOrderResBO();
            // 延迟退款处理，实退金额返回0
            refundOrderResBO.setRefundTypeEnum(ChannelRefundTypeEnum.ALL);
            result.setData(refundOrderResBO);
            return result;
        } else {
            // 全额退款
            umsRefundOrderParam.setRefundOrderId(refundOrderBO.getRefundId());
            umsRefundOrderParam.setRefundAmount(refundOrderBO.getAmount() - refundOrderBO.getRefundPlatformDiscount());
            Result<RefundOrderResBO> result = refundAll(umsRefundOrderParam, refundOrderBO, umsMopProperties);
            log.info("银联全额款返回:{}", JsonUtil.toJsonString(result));
            if (null == result.getData()) {
                result.setData(new RefundOrderResBO());
            }
            result.getData().setRefundTypeEnum(ChannelRefundTypeEnum.ALL);
            return result;
        }

    }

    /**
     * 退款查询
     *
     * @param getRefundOrderBO {@link GetRefundOrderBO}
     * @return {@link Result<GetRefundOrderResBO>}
     * @throws BusinessException
     */
    @Override
    public Result<GetRefundOrderResBO> getRefund(GetRefundOrderBO getRefundOrderBO) throws BusinessException {
        UmsMopGetRefundOrderRequest umsGetRefundOrderParam = new UmsMopGetRefundOrderRequest();
        final UmsMopProperties umsMopProperties = umsMopAllConfig.getByOrgPayChannelId(getRefundOrderBO.getOrgPaymentChannelId());
        umsGetRefundOrderParam.setRequestTimestamp(DateTimeUtil.temporalFormat2String(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));

        QueryWrapper<PaymentRefundFlow> conditions = new QueryWrapper<>();
        conditions.eq("user_id", getRefundOrderBO.getUid());
        conditions.eq("flow_id", getRefundOrderBO.getRefundId());
        PaymentRefundFlow refundFlow = paymentRefundFlowMapper.selectOne(conditions);
        Assert.isTrue(null != refundFlow, "未查询到相应退款流水");

        QueryWrapper<PaymentOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("flow_id", refundFlow.getPayFlowId());
        queryWrapper.eq("user_id", getRefundOrderBO.getUid());
        PaymentOrder paymentOrder = paymentOrderMapper.selectOne(queryWrapper);
        Assert.isTrue(null != paymentOrder, "未查询到相应支付流水");
        Assert.isTrue(!StringUtils.isEmpty(paymentOrder.getPaymentWay()), "流水支付方式异常");

        Map<String, String> mtiMap = handleMidTidInstMid(paymentOrder.getPayScene(), paymentOrder.getPaymentWay(), umsMopProperties);
        umsGetRefundOrderParam.setMid(mtiMap.get("mid"));
        umsGetRefundOrderParam.setTid(mtiMap.get("tid"));

        umsGetRefundOrderParam.setMerOrderId(getRefundOrderBO.getRefundId());

        log.info("ums_getRefund_url:{}, params:{}", umsMopProperties.getApiUrl(), JsonUtil.toJsonString(umsGetRefundOrderParam));
        Result<GetRefundOrderResBO> result = new Result<>();
        try {
            final UmsMopGetRefundOrderResponse refundOrderResult = new UmsMopHttpUtils<UmsMopGetRefundOrderResponse>(umsMopProperties, "v1/netpay/refund-query").doPostToken(JsonUtil.toJsonString(umsGetRefundOrderParam), UmsMopGetRefundOrderResponse.class);
            log.info("银联mop退款查询返回:{}", JsonUtil.toJsonString(refundOrderResult));

            result.setRet(refundOrderResult.isOK() ? 0 : 1);
            result.setMsg(StringUtils.isNotEmpty(refundOrderResult.getErrInfo()) ? refundOrderResult.getErrInfo() : refundOrderResult.getErrMsg());

            GetRefundOrderResBO resBO = new GetRefundOrderResBO();
            resBO.setRefundId(refundOrderResult.getRefundOrderId());
            resBO.setTransactionId(refundOrderResult.getSeqId());
            resBO.setTradeNo(refundOrderResult.getTargetOrderId());
            // 退款时间，银联未返回，使用退款流水表中退款时间
            resBO.setRefundTime(refundFlow.getFlowTime());

            // 退款状态
            resBO.setChannelOrderStatusEnum(ChannelOrderStatusEnum.fail);
            switch (UmsRefundStatusEnum.getByValue(refundOrderResult.getRefundStatus())) {
                case PROCESSING:
                    resBO.setChannelOrderStatusEnum(ChannelOrderStatusEnum.ing);
                    break;
                case FAIL:
                case UNKNOWN:
                    resBO.setChannelOrderStatusEnum(ChannelOrderStatusEnum.fail);
                    break;
                case SUCCESS:
                    resBO.setChannelOrderStatusEnum(ChannelOrderStatusEnum.success);
                    break;
            }
            resBO.setAmount(refundOrderResult.getRefundInvoiceAmount());
        } catch (Exception e) {
            log.error(e.getMessage());
            result.setRet(1);
            result.setMsg("查询退款失败");
        }
        return result;
    }

    /**
     * 关闭订单
     *
     * @param closeOrderBO {@link CloseOrderBO}
     * @return {@link Result}
     * @throws BusinessException
     */
    @Override
    public Result<?> close(CloseOrderBO closeOrderBO) throws BusinessException {
        UmsMopCloseOrderRequest umsCloseOrderParam = new UmsMopCloseOrderRequest();
        final UmsMopProperties umsMopProperties = umsMopAllConfig.getByOrgPayChannelId(closeOrderBO.getOrgPaymentChannelId());
        umsCloseOrderParam.setRequestTimestamp(DateTimeUtil.temporalFormat2String(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));

        QueryWrapper<PaymentOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("flow_id", closeOrderBO.getOrderId());
        queryWrapper.eq("user_id", closeOrderBO.getUid());
        PaymentOrder paymentOrder = paymentOrderMapper.selectOne(queryWrapper);
        Assert.isTrue(null != paymentOrder, "未查询到相应支付流水");
        Assert.isTrue(!StringUtils.isEmpty(paymentOrder.getPaymentWay()), "流水支付方式异常");

        if (paymentOrder.getPayScene().equalsIgnoreCase(PaySceneEnum.SCAN.getCode())) {
            umsCloseOrderParam.setQrCodeId(closeOrderBO.getOrderId());
        } else {
            umsCloseOrderParam.setMerOrderId(closeOrderBO.getOrderId());
        }

        Map<String, String> mtiMap = handleMidTidInstMid(paymentOrder.getPayScene(), paymentOrder.getPaymentWay(), umsMopProperties);
        umsCloseOrderParam.setMid(mtiMap.get("mid"));
        umsCloseOrderParam.setTid(mtiMap.get("tid"));
        umsCloseOrderParam.setInstMid(mtiMap.get("instMid"));

        log.info("ums_mop_close_url:{}, params:{}", umsMopProperties.getApiUrl(), JsonUtil.toJsonString(umsCloseOrderParam));
        Result<?> result = new Result<>();
        try {
            final UmsMopCloseOrderRespponse closeOrderResult = new UmsMopHttpUtils<UmsMopCloseOrderRespponse>(umsMopProperties, "v1/netpay/close")
                    .doPostToken(JsonUtil.toJsonString(umsCloseOrderParam), UmsMopCloseOrderRespponse.class);
            log.info("银联mop关闭订单返回:{}", JsonUtil.toJsonString(closeOrderResult));
            result.setRet(closeOrderResult.isOK() ? 0 : 1);
            result.setMsg(StringUtils.isNotEmpty(closeOrderResult.getErrInfo()) ? closeOrderResult.getErrInfo() : closeOrderResult.getErrMsg());
        } catch (Exception e) {
            log.error(e.getMessage());
            result.setRet(1);
            result.setMsg("关闭订单失败");
        }
        return result;
    }
    /**
     * 处理支付结果
     *
     * @param payResult {@link Map}
     * @return {@link PayResultBO}
     */
    @Override
    public PayResultBO payResult(Map<String, Object> payResult) {
        //  消息验证sign
//        this.callbackMsgCheck(payResult);
        BusinessErrorInfoEnum.UMS_CHANNEL_SIGN_FAIL.assertIsTrue(this.callbackMsgCheck(payResult));
        PayResultBO payResultBO = new PayResultBO();
        //TODO 待确认订单状态，是否需要再次发起查询
        BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_UNKNOWN_STATUS.assertIsTrue((payResult.containsKey("status") && payResult.get("status").toString().equalsIgnoreCase("TRADE_SUCCESS")) ||
                (payResult.get("billStatus").toString().equalsIgnoreCase("PAID")));
        payResultBO.setStatus("success");
        //payResultBO.setUid(String.valueOf(payResult.get("buyerId")));
        if (payResult.containsKey("merOrderId")) {
            payResultBO.setOrderId(String.valueOf(payResult.get("merOrderId")));
        } else if (payResult.containsKey("billNo")) {
            payResultBO.setOrderId(String.valueOf(payResult.get("billNo")));
        }
        LocalDateTime payTime = LocalDateTime.now();
        if (payResult.containsKey("targetOrderId")) {
            payResultBO.setTransactionId(String.valueOf(payResult.get("targetOrderId")));
            // 处理优惠金额
            // 总金额
            int totalAmount = Integer.parseInt(String.valueOf(payResult.get("totalAmount")));
            int couponAmount = 0;
            // 实付金额
            if (payResult.containsKey("buyerPayAmount") && !StringUtils.isEmpty(MapUtils.getString(payResult, "buyerPayAmount"))) {
                int buyerPayAmount = Integer.parseInt(payResult.get("buyerPayAmount").toString());
                if (buyerPayAmount > 0) {
                    couponAmount = totalAmount - buyerPayAmount;
                }
            }
            // 实际支付金额
            payResultBO.setAmount(totalAmount - couponAmount);
            // 优惠金额
            payResultBO.setCouponAmount(couponAmount);
            payResultBO.setMetadata(String.valueOf(payResult.get("attachedData")));
            if (payResult.containsKey("payTime") && payResult.get("payTime") != null) {
                payTime = DateTimeUtil.string2LocalDateTime(payResult.get("payTime").toString(), "yyyy-MM-dd HH:mm:ss");
            }

        } else if (payResult.containsKey("billPayment") && payResult.get("billPayment") != null) {
            JSONObject billPaymentJson = JSON.parseObject(payResult.get("billPayment").toString());
            payResultBO.setTransactionId(billPaymentJson.getString("targetOrderId"));
            //buyerCashPayAmt,云闪付会返回这个字段
            // 总金额
            Integer totalAmount = billPaymentJson.getInteger("totalAmount");
            int couponAmount = 0;
            // 实付金额
            if (billPaymentJson.containsKey("buyerCashPayAmt") && !StringUtils.isEmpty(billPaymentJson.getString("buyerCashPayAmt"))) {
                Integer buyerCashPayAmt = Integer.valueOf(billPaymentJson.get("buyerCashPayAmt").toString());
                if (buyerCashPayAmt > 0) {
                    couponAmount = totalAmount - buyerCashPayAmt;
                }
            }
            // 实际支付金额
            payResultBO.setAmount(totalAmount - couponAmount);
            // 优惠金额
            payResultBO.setCouponAmount(couponAmount);
            payResultBO.setMetadata(billPaymentJson.getString("billDesc"));
            if (billPaymentJson.containsKey("payTime") && billPaymentJson.get("payTime") != null) {
                payTime = DateTimeUtil.string2LocalDateTime(billPaymentJson.getString("payTime"), "yyyy-MM-dd HH:mm:ss");
            }
        }
        payResultBO.setTradeNo(payResultBO.getOrderId());
        payResultBO.setPayTime(payTime);
        return payResultBO;
    }

    /**
     * 处理退款结果
     *
     * @param refundResult {@link Map}
     * @return {@link RefundResultBO}
     */
    @Override
    public RefundResultBO refundResult(Map<String, Object> refundResult) {
        try {
            //  消息验证sign
            BusinessErrorInfoEnum.UMS_CHANNEL_SIGN_FAIL.assertIsTrue(this.callbackMsgCheck(refundResult));
            // TODO 里面的退款单号
            RefundResultBO refundResultBO = new RefundResultBO();
            refundResultBO.setStatus("success");
            if (refundResult.containsKey("refundOrderId")) {
                refundResultBO.setRefundId(String.valueOf(refundResult.get("refundOrderId")));
            }

            refundResultBO.setTransactionId(String.valueOf(refundResult.get("seqId")));
            refundResultBO.setTradeNo(refundResult.containsKey("billNo") && refundResult.get("billNo") != null ? String.valueOf(refundResult.get("billNo")) : null);
            refundResultBO.setAmount(Integer.valueOf(String.valueOf(refundResult.get("refundAmount"))));

            LocalDateTime refundPayTime = LocalDateTime.now();
            if (refundResult.containsKey("refundPayTime") && refundResult.get("refundPayTime") != null) {
                refundPayTime = DateTimeUtil.string2LocalDateTime(refundResult.get("refundPayTime").toString(), "yyyy-MM-dd HH:mm:ss");
            }
            refundResultBO.setRefundTime(refundPayTime);
            return refundResultBO;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_ERROR, null, e.getMessage(), e);
        }
    }

    @Override
    public Result settleV2(List<PaymentFlowDetailSettle> detailSettles, List<PaymentFlowDetailSettleDetail> detailSettlesDetails) throws BusinessException {
        if (!CollectionUtils.isEmpty(detailSettles)) {
            return Result.fail("划账数据为空！");
        }
        return Result.success();
        /*final List<PaymentOrder> paymentOrderList = paymentOrderMapper.selectList(new LambdaQueryWrapper<PaymentOrder>().in(PaymentOrder::getFlowId, detailSettles.stream().map(PaymentFlowDetailSettle::getFlowId).collect(Collectors.toSet())));
        // 生成批次号
        // 结算批次号
        final String settleBatchNo = PaymentIdHelper.getSettleBatchNo(detailSettles.get(0).getOrgPaymentChannelId());
        try {
            // 根据流水号来分组，较少调用银商的次数
            final Map<String, List<PaymentFlowDetailSettle>> flowGroups = detailSettles.stream().collect(Collectors.groupingBy(PaymentFlowDetailSettle::getFlowId));
            flowGroups.forEach((flowId, flows) -> {
                final PaymentOrder paymentOrder = paymentOrderList.stream().filter(f -> f.getFlowId().equals(flowId)).findFirst().get();
                final UmsMopProperties umsMopProperties = umsMopAllConfig.getByOrgPayChannelId(paymentOrder.getOrgPaymentChannelId());
                final UmsMopSettleConfirmRequest request = new UmsMopSettleConfirmRequest();
                Map<String, String> mtiMap = handleMidTidInstMid(paymentOrder.getPayScene(), paymentOrder.getPaymentWay(), umsMopProperties);
                request.setRequestTimestamp(DateTimeUtil.temporalFormat2String(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));
                request.setMid(mtiMap.get("mid"));
                request.setTid(mtiMap.get("tid"));
                request.setInstMid(mtiMap.get("instMid"));
                request.setMerOrderId(flowId);
                final int totalPlatformAmount = flows.stream().mapToInt(PaymentFlowDetailSettle::getPlatformAmount).sum();
                request.setPlatformAmount((long)totalPlatformAmount);

                final List<UmsMopSettleConfirmRequest.UmsMopSettleConfirmSubOrder> subOrders = new ArrayList<>();
                final UmsMopSettleConfirmRequest.UmsMopSettleConfirmSubOrder suborder = new UmsMopSettleConfirmRequest.UmsMopSettleConfirmSubOrder();
                final List<PaymentFlowDetailSettleDetail> details = detailSettlesDetails.stream().filter(d -> flows.stream().map(PaymentFlowDetailSettle::getSettleFlowId).collect(Collectors.toSet()).contains(d.getSettleFlowId())).toList();

                for (PaymentFlowDetailSettleDetail detail : details) {
                    // 子商户号
                    suborder.setMid(detail.getSubMerchantId());
                    suborder.setMerOrderId(detail.getSubFlowId());
                    suborder.setTotalAmount(detail.getAmount());
                    subOrders.add(suborder);
                }
                request.setSubOrders(subOrders);
                final UmsMopSettleConfirmResponse settleConfirmResult;
                try {
                    settleConfirmResult = new UmsMopHttpUtils<UmsMopSettleConfirmResponse>(umsMopProperties, "sub-orders-confirm")
                            .doPostToken(JsonUtil.toJsonString(request), UmsMopSettleConfirmResponse.class);
                    log.info("银联mop订单确认分账返回:{}", JsonUtil.toJsonString(settleConfirmResult));
                    if(!settleConfirmResult.isOK()){
                        // TODO 失败处理
                        log.info("error");
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
            return Result.success();
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_CALLBACK_REFUND_ERROR, null, e.getMessage(), e);
        }*/
    }

    @Override
    public boolean autoQueryStatus(Long orgPaymentChannelId) {
        return false;
    }

    @Override
    public PayOrderAutoQueryConfig getOrderAutoQueryConfig(Long orgPaymentChannelId) {
        return null;
    }

    /**
     * 各支付场景下各支付方式参数处理
     *
     * @param umsUnifiedOrderParam {@link UmsUnifiedOrderParam}
     * @param unifiedOrderBO       {@link UnifiedOrderBO}
     */
    private void handlePayWay(UmsMopUnifiedOrderRequest umsUnifiedOrderParam, UnifiedOrderBO unifiedOrderBO, UmsMopProperties umsMopProperties) {
        // 1、app中支付
        if (PaySceneEnum.APP.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene())) {
            // 微信APP支付
            if (PaymentWayEnum.WECHAT.getCode().equalsIgnoreCase(unifiedOrderBO.getPayWay())) {
                umsUnifiedOrderParam.setTradeType("MINI");
                umsUnifiedOrderParam.setMid(umsMopProperties.getMidMini());
                umsUnifiedOrderParam.setTid(umsMopProperties.getTidMini());
                umsUnifiedOrderParam.setInstMid(umsMopProperties.getInstMidMini());
                if (!StringUtils.isEmpty(unifiedOrderBO.getSubAppId())) {
                    umsUnifiedOrderParam.setSubAppId(unifiedOrderBO.getSubAppId());
                } else {
                    umsUnifiedOrderParam.setSubAppId(umsMopProperties.getMiniAppId());
                }
                umsUnifiedOrderParam.setSubOpenId(unifiedOrderBO.getOpenid()); // 用户在平台中的openid
                return;
            }

            umsUnifiedOrderParam.setTradeType("APP");
            umsUnifiedOrderParam.setMid(umsMopProperties.getMidApp());
            umsUnifiedOrderParam.setTid(umsMopProperties.getTidApp());
            umsUnifiedOrderParam.setInstMid(umsMopProperties.getInstMidApp());
            // 支付宝
            if (PaymentWayEnum.ALIPAY.getCode().equalsIgnoreCase(unifiedOrderBO.getPayWay())) {
                umsUnifiedOrderParam.setUserId(unifiedOrderBO.getOpenid()); // 支付宝买家id
                return;
            }
        }

        // 2、微信小程序中支付 - 只支持微信支付方式
        if (PaySceneEnum.MINIAPP.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene())) {
            /*if (!PaymentWayEnum.WECHAT.getCode().equalsIgnoreCase(unifiedOrderBO.getPayWay())) {
                throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_PAY_FAIL, null, "该场景不支持该支付方式");
            }*/
            umsUnifiedOrderParam.setTradeType("MINI");
            if (PaymentWayEnum.WECHAT.getCode().equals(unifiedOrderBO.getPayWay())) {
                umsUnifiedOrderParam.setMid(umsMopProperties.getMidMini());
                umsUnifiedOrderParam.setTid(umsMopProperties.getTidMini());
            } else if (PaymentWayEnum.ALIPAY.getCode().equals(unifiedOrderBO.getPayWay())) {
                umsUnifiedOrderParam.setMid(umsMopProperties.getMidMini());
                umsUnifiedOrderParam.setTid(umsMopProperties.getTidMini());
                umsUnifiedOrderParam.setUserId(unifiedOrderBO.getOpenid());
            } else if (PaymentWayEnum.UNIONPAY.getCode().equals(unifiedOrderBO.getPayWay())) {
                umsUnifiedOrderParam.setMid(umsMopProperties.getMidMini());
                umsUnifiedOrderParam.setTid(umsMopProperties.getTidMini());
            } else {
                BusinessErrorInfoEnum.SYSTEM_ERROR.assertFail("小程序场景不支持的支付方式" + unifiedOrderBO.getPayWay());
            }
            umsUnifiedOrderParam.setInstMid(umsMopProperties.getInstMidMini());
            if (!StringUtils.isEmpty(unifiedOrderBO.getSubAppId())) {
                umsUnifiedOrderParam.setSubAppId(unifiedOrderBO.getSubAppId());
            } else {
                umsUnifiedOrderParam.setSubAppId(umsMopProperties.getMiniAppId());
            }
            umsUnifiedOrderParam.setSubOpenId(unifiedOrderBO.getOpenid()); // 用户在平台中的openid
            return;
        }

        // 3、微信公众号h5中支付 - 只支持微信支付方式
        if (PaySceneEnum.WXAPP.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene())) {
            if (!PaymentWayEnum.WECHAT.getCode().equalsIgnoreCase(unifiedOrderBO.getPayWay())) {
                throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_PAY_FAIL, null, "该场景不支持该支付方式");
            }
            umsUnifiedOrderParam.setMid(umsMopProperties.getMidWxApp());
            umsUnifiedOrderParam.setTid(umsMopProperties.getTidWxApp());
            umsUnifiedOrderParam.setInstMid(umsMopProperties.getInstMidWxApp());
            if (!StringUtils.isEmpty(unifiedOrderBO.getSubAppId())) {
                umsUnifiedOrderParam.setSubAppId(unifiedOrderBO.getSubAppId());
            } else {
                umsUnifiedOrderParam.setSubAppId(umsMopProperties.getMiniAppId());
            }
            umsUnifiedOrderParam.setSubOpenId(unifiedOrderBO.getOpenid()); // 用户在平台中的openid
            return;
        }

        // 4、h5中支付
        if (PaySceneEnum.H5.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene())) {
            if (PaymentWayEnum.WECHAT.getCode().equalsIgnoreCase(unifiedOrderBO.getPayWay())) {
                throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_PAY_FAIL, null, "该场景不支持该支付方式");
            }

            umsUnifiedOrderParam.setMid(umsMopProperties.getMidApp());
            umsUnifiedOrderParam.setTid(umsMopProperties.getTidApp());
            umsUnifiedOrderParam.setInstMid(umsMopProperties.getInstMidH5());
            return;
        }

        // 5、扫码支付
        if (PaySceneEnum.SCAN.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene())) {
            umsUnifiedOrderParam.setMid(umsMopProperties.getMidScan());
            umsUnifiedOrderParam.setTid(umsMopProperties.getTidScan());
            umsUnifiedOrderParam.setInstMid(umsMopProperties.getInstMidScan());
        }

        // 6、云闪付小程序
        if (PaySceneEnum.UNION_MINI.getCode().equalsIgnoreCase(unifiedOrderBO.getPayScene())) {
            umsUnifiedOrderParam.setMid(umsMopProperties.getMidMini());
            umsUnifiedOrderParam.setTid(umsMopProperties.getTidMini());
            umsUnifiedOrderParam.setInstMid(umsMopProperties.getInstMidMini());
//            if (!StringUtils.isEmpty(unifiedOrderBO.getSubAppId())) {
//                umsUnifiedOrderParam.setSubAppId(unifiedOrderBO.getSubAppId());
//            } else {
//                umsUnifiedOrderParam.setSubAppId(umsProperties.getMiniAppId());
//            }
//            umsUnifiedOrderParam.setSubOpenId(unifiedOrderBO.getOpenid()); // 用户在平台中的openid
//            return;
        }
    }

    /**
     * 获取银商-天满请求url
     *
     * @param payScene
     * @param payWay
     * @return
     */
    private String getUmsMopUrl(String payScene, String payWay) {
        if (PaySceneEnum.APP.getCode().equals(payScene) || PaySceneEnum.MINIAPP.getCode().equals(payScene)) {
            // app支付
            if (PaymentWayEnum.WECHAT.getCode().equalsIgnoreCase(payWay)) {
                return "v1/netpay/wx/unified-order";
            } else if (PaymentWayEnum.ALIPAY.getCode().equalsIgnoreCase(payWay)) {
                return "v1/netpay/trade/create";
            } else if (PaymentWayEnum.UNIONPAY.getCode().equalsIgnoreCase(payWay)) {
                return "v1/netpay/uac/mini-order";
            }
        } else if (PaySceneEnum.H5.getCode().equals(payScene)) {
            // 小程序支付
            if (PaymentWayEnum.WECHAT.getCode().equalsIgnoreCase(payWay)) {
                return "v1/netpay/wxpay/h5-pay";
            } else if (PaymentWayEnum.ALIPAY.getCode().equalsIgnoreCase(payWay)) {
                return "v1/netpay/trade/h5-pay";
            } else if (PaymentWayEnum.UNIONPAY.getCode().equalsIgnoreCase(payWay)) {
                return "v1/netpay/netpay/uac/order";
            }
        }
        return null;

    }

    /**
     * 根据支付下单场景和支付方式处理mid和tid
     *
     * @param payScene 支付场景
     * @param payWay   支付方式
     * @return {@link Map}
     */
    private Map<String, String> handleMidTidInstMid(String payScene, String payWay, UmsMopProperties umsMopProperties) {
        Map<String, String> map = new HashMap<>();
        // 1、平台app中支付
        if (PaySceneEnum.APP.getCode().equalsIgnoreCase(payScene)) {
            // 微信
            if (PaymentWayEnum.WECHAT.getCode().equalsIgnoreCase(payWay)) {
                map.put("mid", umsMopProperties.getMidMini());
                map.put("tid", umsMopProperties.getTidMini());
                map.put("instMid", umsMopProperties.getInstMidMini());
                return map;
            }

            map.put("mid", umsMopProperties.getMidApp());
            map.put("tid", umsMopProperties.getTidApp());
            map.put("instMid", umsMopProperties.getInstMidApp());
            return map;
        }

        // 2、微信小程序中支付
        if (PaySceneEnum.MINIAPP.getCode().equalsIgnoreCase(payScene)) {
            map.put("mid", umsMopProperties.getMidMini());
            map.put("tid", umsMopProperties.getTidMini());
            map.put("instMid", umsMopProperties.getInstMidMini());
            return map;
        }

        // 3、微信公众号h5中支付
        if (PaySceneEnum.WXAPP.getCode().equalsIgnoreCase(payScene)) {
            map.put("mid", umsMopProperties.getMidWxApp());
            map.put("tid", umsMopProperties.getTidWxApp());
            map.put("instMid", umsMopProperties.getInstMidWxApp());
            return map;
        }

        // 4、h5中支付
        if (PaySceneEnum.H5.getCode().equalsIgnoreCase(payScene)) {
            map.put("mid", umsMopProperties.getMidApp());
            map.put("tid", umsMopProperties.getTidApp());
            map.put("instMid", umsMopProperties.getInstMidH5());
            return map;
        }

        // 5、扫码支付
        if (PaySceneEnum.SCAN.getCode().equalsIgnoreCase(payScene)) {
            map.put("mid", umsMopProperties.getMidScan());
            map.put("tid", umsMopProperties.getTidScan());
            map.put("instMid", umsMopProperties.getInstMidScan());
            return map;
        }
        // 6、云闪付小程序
        if (PaySceneEnum.UNION_MINI.getCode().equalsIgnoreCase(payScene)) {
            map.put("mid", umsMopProperties.getMidMini());
            map.put("tid", umsMopProperties.getTidMini());
            map.put("instMid", umsMopProperties.getInstMidMini());
            return map;
        }
        return map;
    }

    /**
     * 根据清分规则计算渠道应核销日期
     *
     * @param settlementRuleResVO {@link SettlementRuleResVO} 清分规则
     * @param startDate           {@link LocalDate} 计算的基点日期
     * @return {@link LocalDate}
     */
    private LocalDate calSettleDate(SettlementRuleResVO settlementRuleResVO, LocalDate startDate) {
        LocalDate settleDate;
        // 清分周期类型-按天
        if (ClearingCycleModeEnum.DATE.getCode().equalsIgnoreCase(settlementRuleResVO.getClearingCycleMode())) {
            settleDate = startDate.plusDays(settlementRuleResVO.getClearingCycleValue());
        } else { // 清分周期类型-按月
            settleDate = startDate.plusMonths(settlementRuleResVO.getClearingCycleValue());
            settleDate = settleDate.withDayOfMonth(settlementRuleResVO.getClearingCycleValueExt());
        }
        return settleDate;
    }

    private boolean callbackMsgCheck(Map<String, Object> msg) {
        if (!msg.containsKey("sign") || StringUtils.isEmpty(MapUtils.getString(msg, "sign"))) {
            log.info("消息内容无sign字段");
            return false;
        }
        boolean checkResult = false;
        try {
            return true;
            /*String umsSign = msg.get("sign").toString();
            Optional<String> sign = buildSignString2(umsMopProperties.getKey(), msg);
            if (!sign.isPresent()) {
                log.info("银联消息验签失败");
                return false;
            }
            checkResult = org.apache.commons.lang3.StringUtils.equalsIgnoreCase(umsSign, sign.get());
            if (!checkResult) {
                log.error("银联消息验签失败");
            }*/
        } catch (Exception e) {
            log.error("银联消息验签失败:{}", e.getMessage());
            checkResult = false;
        }

        return checkResult;
    }


    /**
     * 2022-07-22 银联反馈不限制子订单部分退款，测试用
     * 一次退款，测试
     *
     * @param umsRefundOrderParam 退款参数
     * @return
     */
    public Result<RefundOrderResBO> refundWithSubOrders(UmsMopRefundOrderRequest umsRefundOrderParam, RefundOrderBO refundOrderBO,
                                                        Map<String, String> subMerchantMap, Map<String, String> subFlowIdMap,
                                                        UmsMopProperties umsMopProperties) {
        BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL_PARAMS_ERROR.assertIsTrue(umsRefundOrderParam != null &&
                refundOrderBO != null);
        if (CollectionUtils.isEmpty(umsRefundOrderParam.getSubOrders())) {
            umsRefundOrderParam.setSubOrders(new ArrayList<>(refundOrderBO.getSubOrders().size()));
        }
        refundOrderBO.getSubOrders().forEach(s -> {
            final UmsMopRefundOrderRequest.SubOrder subOrder = new UmsMopRefundOrderRequest.SubOrder();
            subOrder.setMid(subMerchantMap.get(s.getSubOrderId()));
            subOrder.setMerOrderId(subFlowIdMap.get(s.getSubOrderId()));
            subOrder.setTotalAmount(s.getAmount() - s.getRefundPlatformDiscount());
            umsRefundOrderParam.getSubOrders().add(subOrder);
        });
        umsRefundOrderParam.setRefundOrderId(refundOrderBO.getRefundId());
        umsRefundOrderParam.setRefundAmount(refundOrderBO.getAmount() - refundOrderBO.getRefundPlatformDiscount());

        BusinessErrorInfoEnum.CHANNEL_REFUND_FAIL_AMOUNT_ERROR.assertIsTrue(umsRefundOrderParam.getRefundAmount().equals(
                umsRefundOrderParam.getSubOrders().stream().mapToInt(UmsMopRefundOrderRequest.SubOrder::getTotalAmount).sum()
        ));
        Result<RefundOrderResBO> result = this.doRefund(umsRefundOrderParam, umsMopProperties);
        if (result.getRet() != 0) {
            log.error("退款失败:{}", result);
            return result;
        }
        if (result.getData() != null && result.getData().getRefundInvoiceAmount() != null) {
            // 实际退款的金额
            PaymentRefundFlow updateRefundFlow = new PaymentRefundFlow();
            updateRefundFlow.setAmount(result.getData().getRefundInvoiceAmount());
            updateRefundFlow.setRefundChannelDiscount(umsRefundOrderParam.getRefundAmount() - result.getData().getRefundInvoiceAmount());
            QueryWrapper<PaymentRefundFlow> conditions = new QueryWrapper<>();
            conditions.eq("flow_id", umsRefundOrderParam.getRefundOrderId());
            conditions.eq("user_id", refundOrderBO.getUid());
            paymentRefundFlowMapper.update(updateRefundFlow, conditions);
            result.getData().setRefundInvoiceAmount(0);// 设置实付金额=0，不再做更新
        }
        return result;
    }


    /**
     * 整单退款
     *
     * @param umsRefundOrderParam 退款参数
     * @return
     */
    public Result<RefundOrderResBO> refundAll(UmsMopRefundOrderRequest umsRefundOrderParam, RefundOrderBO refundOrderBO, UmsMopProperties umsMopProperties) {
        Result<RefundOrderResBO> result = this.doRefund(umsRefundOrderParam, umsMopProperties);
        if (result.getRet() != 0) {
            log.error("整单退款失败:{}", result);
            return result;
        }
        if (result.getData() != null && result.getData().getRefundInvoiceAmount() != null) {
            // 实际退款的金额
            PaymentRefundFlow updateRefundFlow = new PaymentRefundFlow();
            updateRefundFlow.setAmount(result.getData().getRefundInvoiceAmount());
            updateRefundFlow.setRefundChannelDiscount(umsRefundOrderParam.getRefundAmount() - result.getData().getRefundInvoiceAmount());
            QueryWrapper<PaymentRefundFlow> conditions = new QueryWrapper<>();
            conditions.eq("flow_id", umsRefundOrderParam.getRefundOrderId());
            conditions.eq("user_id", refundOrderBO.getUid());
            paymentRefundFlowMapper.update(updateRefundFlow, conditions);
            result.getData().setRefundInvoiceAmount(0);// 设置实付金额=0，不再做更新
        }
        return result;
    }

    /**
     * 执行退款操作
     *
     * @param umsRefundOrderParam 退款参数
     * @return
     */
    public Result<RefundOrderResBO> doRefund(UmsMopRefundOrderRequest umsRefundOrderParam, UmsMopProperties umsMopProperties) {
        String refundUrl = "v1/netpay/refund";
        log.info("ums_refund_url:{}, params:{}", refundUrl, JsonUtil.toJsonString(umsRefundOrderParam));
        Result<RefundOrderResBO> result = new Result<>();
        try {
            final UmsMopRefundOrderResponse refundOrderResult = new UmsMopHttpUtils<UmsMopRefundOrderResponse>(umsMopProperties, refundUrl).doPostToken(JsonUtil.toJsonString(umsRefundOrderParam), UmsMopRefundOrderResponse.class);
            log.info("银联订单退款返回:{}", JsonUtil.toJsonString(refundOrderResult));
            result.setRet(UmsErrCodeEnum.SUCCESS.getCode().equalsIgnoreCase(refundOrderResult.getErrCode()) ? 0 : 1);
            result.setMsg(refundOrderResult.getErrInfo());
            if (refundOrderResult.getRefundInvoiceAmount() != null && refundOrderResult.getRefundInvoiceAmount() != 0
                    && !refundOrderResult.getRefundInvoiceAmount().equals(refundOrderResult.getRefundAmount())) {
                RefundOrderResBO refundOrderResBO = new RefundOrderResBO();
                // 实付部分退款金额
                refundOrderResBO.setRefundInvoiceAmount(refundOrderResult.getRefundInvoiceAmount());
                result.setData(refundOrderResBO);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            result.setRet(1);
            result.setMsg("退款失败");
        }
        return result;
    }
}
